//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include "engine.h"

STATIC INLINE ECode EnsureCapacity(
    /* [in, out] */ FusionVector* vector)
{
    assert(vector->count <= vector->capacity);

    if (!vector->elements) {
        UInt32  size = vector->capacity * sizeof(Void *);
        vector->elements = (Void **)malloc(size);
        if (!vector->elements) {
            GFX_EXPRESSION(ERRORLEVEL,
                kprintf("Ensure capacity not enough memory\n"));

            return E_OUT_OF_MEMORY;
        }

        vector->order = 0;
    }
    else if (vector->count == vector->capacity) {
        Void* elements;
        Void* oldelements = vector->elements;
        int capacity = vector->capacity << 1;

        UInt32 size = capacity * sizeof(Void *);
        elements = (Void **)malloc(size);
        if (!elements) {
            GFX_EXPRESSION(ERRORLEVEL,
                kprintf("Ensure capacity not enough memory\n"));

            return E_OUT_OF_MEMORY;
        }

        memcpy(elements, oldelements, vector->count * sizeof(Void *));

        free(oldelements);

        vector->elements = (Void **)elements;
        vector->capacity = capacity;
        vector->order = 0;
    }

    return NOERROR;
}

Void FusionVectorInit(
    /* [in, out] */ FusionVector* vector,
    /* [in] */ Int32 capacity)
{
    assert(capacity > 0);
    assert(vector);
    vector->elements = NULL;
    vector->count = 0;
    vector->capacity = capacity;
}

Void FusionVectorDestroy(
    FusionVector* vector)
{
    assert(vector);

    if (vector->elements) {
        free(vector->elements);
        vector->elements = NULL;
    }

    vector->count = 0;
    vector->capacity = 0;
}

ECode FusionVectorAdd(
    FusionVector * vector,
    Void * element)
{
    ECode   ec;

    // Make sure there's a free entry left.
    //
    ec = EnsureCapacity(vector);
    if (GFX_FAILED(ec)) {
        return ec;
    }

    // Add the element to the vector.
    //
    assert(vector->count < vector->capacity);
    vector->elements[vector->count++] = element;

    return NOERROR;
}

ECode FusionVectorInsert(
    FusionVector * vector,
    Void * element,
    Int32 index)
{
    ECode   ec;

    // Make sure there's a free entry left.
    //
    ec = EnsureCapacity(vector);
    if (GFX_FAILED(ec)) {
        return ec;
    }

    // Move elements from insertion point one up.
    //
    assert(index <= vector->count);
    assert(index + 1 < vector->capacity);
    assert(vector->count < vector->capacity);
    memmove(&vector->elements[index + 1],
            &vector->elements[index],
            (vector->count - index) * sizeof(Void *));

    // Insert the element into the vector.
    //
    vector->elements[index] = element;

    // Increase the element counter.
    //
    vector->count++;
    assert(vector->count <= vector->capacity);

    return NOERROR;
}

ECode FusionVectorMove(
    FusionVector * vector,
    Int32 from,
    Int32 to)
{
    Void  * element;

    assert(from < vector->count && to < vector->count);

    if (to == from)
        return NOERROR;

    // Save the element.
    //
    assert(from < vector->capacity);
    element = vector->elements[from];

    // Move elements that lie on the way to the new position.
    //
    if (to > from) {
        // Element is moving up -> move other elements down.
        //
        assert(to < vector->capacity);
        memmove(&vector->elements[from],
                &vector->elements[from + 1],
                (to - from) * sizeof(Void *));
    }
    else {
        // Element is moving down -> move other elements up.
        //
        assert(from < vector->capacity);
        memmove(&vector->elements[to + 1],
                &vector->elements[to],
                (from - to) * sizeof(Void *));
    }

    // Restore the element at the new position.
    //
    assert(to < vector->capacity);
    vector->elements[to] = element;

    return NOERROR;
}

Void FusionVectorRemove(
    FusionVector * vector,
    Int32 index)
{
    if (index < 0 && index >= vector->count && vector->count > 0) {
        GFX_EXPRESSION(ERRORLEVEL,
            kprintf("Remove index out of the region\n"));
        return;
    }

    // Move elements after this element one down.
    //
    assert(index < vector->capacity);
    memmove(&vector->elements[index],
            &vector->elements[index + 1],
            (vector->count - index - 1) * sizeof(Void *));

    // Decrease the element counter.
    //
    vector->count--;
    assert(vector->count >= 0);
}

ECode FusionVectorRemoveLast(
    FusionVector * vector)
{
    // Decrease the element counter.
    //
    vector->count--;
    assert(vector->count >= 0);

    return NOERROR;
}
